use crate::db::db_knowledge_progress;
use crate::db::entities::knowledge_progress::{Model, SearchResult};
use crate::rest_api::api_knowledge_progress::{CreateParams, KnowledgeProgress, SearchParams};
use crate::rest_api::service::srv_scheduler::validate_scheduler_id;
use crate::rest_api::service::{srv_category, srv_knowledge, srv_scheduler, srv_subject};

use anyhow::Result;

use rbase::define::{ApiError, BaseError};
use rbase::id_utils::{id2str, str2id};
use rbase_iam::db::define::SYSTEM_ID;
use rbase_iam::rest_api::api_user::UserToken;
use rbase_iam::rest_api::service::srv_tag;

pub async fn knowledge_from_model(knowledge_model: &Model) -> Result<KnowledgeProgress> {
    let knowledge = KnowledgeProgress::from_model(knowledge_model);
    Ok(knowledge)
}

pub async fn knowledge_from_result(knowledge_model: &SearchResult) -> Result<KnowledgeProgress> {
    let knowledge = KnowledgeProgress {
        id: id2str(knowledge_model.id),
        name: knowledge_model.name.clone(),
        scheduler_id: match knowledge_model.scheduler_id {
            Some(id) => Some(id2str(id)),
            None => None,
        },
        knowledge_id: match knowledge_model.knowledge_id {
            Some(id) => Some(id2str(id)),
            None => None,
        },
        learned_count: knowledge_model.learned_count,
        learning_point: knowledge_model.learning_point,
        created_at: knowledge_model.created_at,
        modified_at: knowledge_model.modified_at,
    };
    Ok(knowledge)
}

pub async fn fing_knowledge_progress_by_id_with_user(user_token: &UserToken, knowledge_progress_id: &String) -> Result<Model, ApiError> {
    let knowledge_progress_id = str2id(knowledge_progress_id)?;
    let model = db_knowledge_progress::find_by_id_and_owner_ids(knowledge_progress_id, &user_token.owner_ids)
        .await?
        .ok_or_else(|| BaseError::ResourceNotFound(knowledge_progress_id.to_string()))?;

    return Ok(model);
}

pub async fn find_knowledge_proogress_by_id(user_token: &UserToken, knowledge_progress_id: &String) -> Result<KnowledgeProgress, ApiError> {
    let knowledge_model = fing_knowledge_progress_by_id_with_user(user_token, knowledge_progress_id).await?;
    Ok(knowledge_from_model(&knowledge_model).await?)
}

pub async fn delete_knowledge_progress_by_id(user_token: &UserToken, knowledge_progress_id: &String) -> Result<(), ApiError> {
    let knowledge_model = fing_knowledge_progress_by_id_with_user(user_token, knowledge_progress_id).await?;
    // 普通用户不能修改系统资源
    if !user_token.is_admin && knowledge_model.owner_id == Some(SYSTEM_ID) {
        return Err(BaseError::PermissionDenied.into());
    }
    db_knowledge_progress::delete_by_id(knowledge_model.id).await?;
    Ok(())
}

pub async fn create_knowledge_progress(user_token: &UserToken, new_knowledge: &CreateParams) -> Result<i64, ApiError> {
    let scheduler_id = validate_scheduler_id(user_token, &Some(new_knowledge.scheduler_id.clone())).await?;
    for knowledge_id_str in new_knowledge.knowledge_ids.iter() {
        let knowledge = srv_knowledge::fing_knowledge_by_id_with_user(user_token, knowledge_id_str).await?;

        let new_model = Model {
            owner_id: Some(user_token.id),
            scheduler_id,
            knowledge_id: Some(knowledge.id),
            ..Default::default()
        };
        db_knowledge_progress::insert_scheduler_knowledge(new_model).await?;
    }
    Ok(new_knowledge.knowledge_ids.len() as i64)
}

pub async fn search(user_token: &UserToken, search_params: &SearchParams) -> Result<(Vec<KnowledgeProgress>, i64)> {
    let scheduler_id = srv_scheduler::validate_scheduler_id(user_token, &search_params.scheduler_id).await?;
    let category_id = srv_category::validate_category_id(user_token, &search_params.category_id).await?;
    let subject_id = srv_subject::validate_subject_id(user_token, &search_params.subject_id).await?;
    let tag_ids = srv_tag::validate_tag_ids(user_token, &search_params.tag_ids).await?;

    let owner_ids = if user_token.is_admin { None } else { Some(vec![SYSTEM_ID, user_token.id]) };
    let (knowledges, total) = db_knowledge_progress::search(&db_knowledge_progress::SearchParams {
        scheduler_id,
        category_id,
        subject_id,
        tag_ids,
        owner_ids,
        name: search_params.name.clone(),
        offset: search_params.offset,
        limit: search_params.limit,
        order_by: search_params.order_by.clone(),
        order: search_params.order.clone(),
        cols: search_params.cols.clone(),
    })
    .await?;

    let mut knowledge_results = Vec::new();
    for knowledge_result in knowledges {
        knowledge_results.push(knowledge_from_result(&knowledge_result).await?);
    }

    Ok((knowledge_results, total))
}
